package com.mg.plugin.agrometeorology.common;

import com.mg.model.Model;
import com.mg.plugin.agrometeorology.entity.StationData;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 降水量计算
 */
public class PrecipitationCalculator {

    /**
     * 全省日平均降水量计算 全省内各站点日降水量的平均
     *
     * @param stationPrecipitations 站点日平均降水量
     * @return 全省日平均降水量
     * @throws Exception 输入数据为空
     */
    public static float calcDayOfPrecipitation(List<StationData> stationPrecipitations) {
        if (null == stationPrecipitations || stationPrecipitations.size() == 0) {
            return 0;
        }
        float totalPrecipitation = 0;
        for (StationData stationData : stationPrecipitations) {
            totalPrecipitation += stationData.getRain();
        }
        return totalPrecipitation / stationPrecipitations.size();
    }

    /**
     * 全省累积降水量计算 全省日平均降水量来计算。计算方法为：从作物播种日期开始，
     * 至每个月的逢5日、10日、15日、20日、25日、30日(或28日、29日、31日)逐次计
     * 算，至收获日结束。
     *
     * @param dayOfPrecipitations 全省日平均降水量
     * @return 全省累积降水量
     * @throws Exception 输入数据为空
     */
    public static float calcAccumulatePrecipitation(List<Float> dayOfPrecipitations) throws Exception {
        if (null == dayOfPrecipitations || dayOfPrecipitations.size() == 0) {
            throw new Exception("calcAccumulatePrecipitation Error");
        }
        float totalPrecipitation = 0;
        for (Float precipitation : dayOfPrecipitations) {
            totalPrecipitation += precipitation;
        }
        return totalPrecipitation;
    }

    /**
     * 全省标准化降水量计算
     * 对全省累积降水量作标准化处理，处理后的降水量便称之为全省标准化降水量。
     *
     * @return
     */
    public static float calcStandardizedPrecipitation(Map<String, List<StationData>> stationDataList, int dateCount, Model model) throws ParseException {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateList = new String[stationDataList.keySet().size()];
        int count = 0;
        for (String date : stationDataList.keySet()) {
            dateList[count] = date;
            count++;
        }

        //时间排序
        for (int i = 1; i < dateList.length; i++) {
            String tmp = dateList[i];//保存当前位置p的元素，其中[0,p-1]已经有序
            int j;
            for (j = i; j > 0 && sdf.parse(tmp).before(sdf.parse(dateList[j - 1])); j--) {
                dateList[j] = dateList[j - 1];//后移一位
            }
            dateList[j] = tmp;//插入到合适的位置
        }
        Date calcDate = DateCalculator.calcTime(sdf.parse(dateList[0]), dateCount);

        //累积降水计算
        List<Float> rains = new ArrayList<Float>();
        float totalRain = 0;
        for (String date : dateList) {
            if (calcDate.before(sdf.parse(date))) {
                continue;
            }
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(sdf.parse(date));
            int month = calendar.get(Calendar.MONTH) + 1;
            int day = calendar.get(Calendar.DAY_OF_MONTH);
            totalRain += calcDayOfPrecipitation(stationDataList.get(date));
            if (month - 4 >= 0) {
                if (isFit(month, day)) {
                    rains.add(totalRain);
                }
            }
        }
        float avg = getAvgPrecipitation(rains, model);
        float result = totalRain / avg;
        if (Float.isInfinite(result) || Float.isNaN(result)) {
            result = 0;
        }
        return result;
    }

    /**
     * 计算累积降水标准差
     *
     * @param rains
     * @param model
     * @return
     */
    private static float getAvgPrecipitation(List<Float> rains, Model model) {
        float total = 0;
        for (float data : rains) {
            total += data;
        }
        float avg = total / rains.size();
        float res = 0;
        for (float data : rains) {
            res += Math.pow((data - avg), 2);
        }
//        model.OutputLog(Model.LogLevel.Info, "getAvgPrecipitation:res=" + res + "avg=" + avg + "ss" + rains.size());
        return (float) Math.sqrt(res / (rains.size() - 1));


    }

    private static boolean isFit(int month, int day) {
        if (day % 5 == 0) {
            if (day != 30) {
                return true;
            }
            if (month == 4 || month == 6) {
                return true;
            }
        }
        if (day == 31) {
            return true;
        }
        return false;
    }

    public static float calcTendaysAccumulatePrecipitation(Map<String, List<StationData>> stationDataList, int i, Model model) throws ParseException {

        if (i % 2 == 1) {
            return 999999.9f;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        float accTemp = 0;
        int count = 0;
        float temp;
        float avgTemp = 0;
        for (String date : stationDataList.keySet()) {
            Date calcDate = DateCalculator.calcTime(sdf.parse(date), i);
            Date beforeDate = DateCalculator.calcTendaysBeforeTime(sdf.parse(date), i);
            if (calcDate.before(sdf.parse(date)) || beforeDate.after(sdf.parse(date))) {
                continue;
            }
            count++;
            List<StationData> data = stationDataList.get(date);
            temp = calcDayOfPrecipitation(data);
            if (!String.valueOf(temp).equals("NaN")) {
                avgTemp = temp;
            }
//            model.OutputLog(Model.LogLevel.Info, "calcAccumulatedTemperature:accTemp=" + accTemp + "avgTemp" + avgTemp);
            accTemp += avgTemp;
        }
//        model.OutputLog(Model.LogLevel.Info, "calcAccumulatedTemperature:accTemp=" + accTemp + "count" + count);
        return accTemp;
    }

    public static float calcTendaysSuitabilityPrecipitation(Map<String, List<StationData>> stationDataList, Map<String, List<StationData>> oldStationDataList, int i, Model model) throws ParseException {

        if (i % 2 == 1) {
            return 999999.9f;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        float accTemp = 0;
        float oldAccTemp = 0;
        int count = 0;
        float temp;
        float oldTemp;
        float avgTemp = 0;
        float oldAvgTemp = 0;
        for (String date : stationDataList.keySet()) {
            Date calcDate = DateCalculator.calcTime(sdf.parse(date), i);
            Date beforeDate = DateCalculator.calcTendaysBeforeTime(sdf.parse(date), i);
            if (calcDate.before(sdf.parse(date)) || beforeDate.after(sdf.parse(date))) {
                continue;
            }
            count++;
            List<StationData> data = stationDataList.get(date);
            List<StationData> oldData = oldStationDataList.get(date);
            temp = calcDayOfPrecipitation(data);
            oldTemp = calcDayOfPrecipitation(oldData);
            if (!String.valueOf(temp).equals("NaN")) {
                avgTemp = temp;
            }
            if (!String.valueOf(oldTemp).equals("NaN")) {
                oldAvgTemp = oldTemp;
            }
//            model.OutputLog(Model.LogLevel.Info, "calcAccumulatedTemperature:accTemp=" + accTemp + "avgTemp" + avgTemp);
            accTemp += avgTemp;
            oldAccTemp += oldAvgTemp;
        }
//        model.OutputLog(Model.LogLevel.Info, "calcAccumulatedTemperature:accTemp=" + accTemp + "count" + count);
        double res = (accTemp - oldAccTemp) / oldAccTemp;
        if (res < -0.3) {
            return accTemp / oldAccTemp;
        } else if (res <= 0.3) {
            return 1;
        }
        return oldAccTemp / accTemp;
    }
}
